import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="For Developers/Creating a Component" />

# Creating a Component

This document lays out step by step guidelines and best practices for creating Radial components. Taking a consistent approach to components will makes the design system easier to learn and use.

## Naming Components

Radial component names use UpperCamelCase, with no special prefix. They're namespaced by their place in the [atomic design heirarchy](./?path=/docs/for-designers-intro--page#atomic-design-principles). To keep atoms extra easy to use, skip extra namespacing and just use the component name. For molecules and organisms, namespace them by putting them in folders, named molecule, or component.

| Filename | Component Syntax |
|---|---|
| button               |   `<Button />`               |
| molecule/byline      |   `<Molecule::Byline />`     |
| organism/newsletter  |   `<Organism::Newsletter />` |

## Creating the component skeleton

If you're starting from scratch, you want to create a basic component skeleton. If you're refactoring an existing component you can skip this step and work with a copy of that component instead.

In Ember you can generate component boilerplate with the following command:

```ember g component button --pod```

This command uses Ember's pods folder layout option to keep the component's template and js file in the same folder.

Now, in the component template, give your component a top level HTML tag, a class, and `...attributes`.
The outer element of your component's class should have the same name as the component, but in kebab-case. (e.g. `<div class="breaking-news">`).
This will gives you a very bare bones component to start with, so storybook doesn't throw errors when you write your stories.

```html
<button class="button" ...attributes></button>
```

## Create stories in Storybook

Storybook allows you to prototype and build out your component with multiple variations visible while you work.

Stories are living examples that demonstrate the different variations of a component. Use design comps as a guide to which stories to include. For example, consider these button designs:

<img alt="List" src="./images/button-design-example.png" width="200" />

Based on these designs you could write stories for the different button types (primary, primary with icon, secondary, icon, and flat) and include variations for the three different sizes within each story.

Stories are written in blocks of code that go directly into your MDX documentation.

```hbs
import { Preview, Story } from '@storybook/addon-docs/blocks';

<Preview withToolbar={true}>
  <Story name="header-default" height="200px">{{
    template: hbs`<Button @label="Button" class="mod-large" />`
  }}</Story>
</Preview>
```

Don't worry if your component code is still only a skeleton, write your stories with the properties and classes you would expect the component to have to show these variations. You can always go back and change it later if your component development takes a different direction.

```hbs
<Button @icon="audio" @iconAlt="Play" class="mod-large"/>
```

For example, even if you don't know if the finished component will end up having a property called `iconAlt`, you can make a guess and you can always change it later.

If you need to mock larger data structures in your stories you can use a context object. A context object is a javascript object containing properties that a story's template can access.

```html
<Preview withToolbar={true}>
  <Story name="header-default" height="200px">{{
    template: hbs`<ArticleHeader @article={{story}} />`,
    context: {
        story: {
            title: 'Scientists Find New Lifeform in Sewers',
            authors: [{name:'Ben Yakas'}]
            // etc...
        }
    }
  }}</Story>
</Preview>
```

Further Reading:

- [Writing MDX stories in Ember](https://github.com/storybookjs/storybook/blob/next/addons/docs/ember/README.md#mdx)


# Writing your component code

## Starting your local development servers

First, start Ember so it can rebuild your component whenever you make changes.

```
ember s
```

Then, start Storybook.

```
yarn storybook
```


With Storybook and Ember running go to Storybook in your browser and navigate to the stories you created. Now you can go back to your component template in your editor of choice. As you update your component's code, its stories will update live in Storybook.


## Styles
  Each component's styles should live in its own file. Add a stylesheet for your component in `/app/styles/_library/components/`. This will be a sass partial, so the filename should start with an underscore. e.g. `app/styles/_library/components/_button.scss`.

  All components are imported into the final stylesheet via `app/styles/_library/_components.scss`. Add an import for the component there.

```scss
//app/styles/_library/_components.scss

@import "./components/button";

```

Every element should have a class. This allows for easier targeting, not just in your stylesheets and themes, but also in automated testing and analytics tools. Use the [Trello styleguide's convention](https://github.com/trello/trellisheets/blob/master/styleguide.md#2-components), `.component-descendant-descendant`, for naming your classes.
```html
<button class="button" ...attributes>
    <Icon class="button-icon" @name={@icon} />
    <span class="button-label">{{@label}}</span>
</button>
```

Provide modifier classes for alternate styling when possible. Again, use the [Trello styleguide's naming convention](https://github.com/trello/trellisheets/blob/master/styleguide.md#modifiers), `.component-descendant.mod-modifier`.

Taking this component template,

```html
<button class="button" ...attributes>{{@labe}}</button>
```

and this component invocation,

```html
<Button class="mod-large" />
```

the `...attributes` syntax will merge the classes, so you can style this button with a selector of `.button.mod-large`.

## Tokens

Use *tokens* to define style values where possible. Cross reference the design comps with the [token list](/docs/for-developers-token-reference--page), and consult with a designer if necessary to help find the appropriate token values.

```scss
.button {}
  padding: var(--space-1) var(--space-4);
  color: rgb(var(--color-button-text));
  background-color: rgb(var(--color-button));
  // ... and so on
}
```

## Optional properties

Sometimes a component has properties that are optional, such as an icon or secondary title. Use a template conditional to hide the elements that display these properties when they don't exist.

```hbs
{{if @icon}}
    <Icon @name={@icon} />
{{/if}}
```

When it makes sense, you can handle even larger variations with template conditionals. When possible, try to keep your components as dumb data containers and avoid unecessary logic in the js file if something can be done in the template. In the following example, the conditional checks for the presence of an href property and changes the base element for the component to a link if it has a link.

```hbs
{{if @href}}
    <a class="button" ...attributes>{{@label}}</a>
{{else}}
    <button class="button" ...attributes>{{@label}}</button>
{{/if}}
```

## States

 Certain states on a component might affect how it's displayed, for example a menu that can be open or closed. Use code to conditionally provide CSS classes that indicates these states as hooks for styling. Again use the [Trello Style guide's Naming convention](https://github.com/trello/trellisheets/blob/master/styleguide.md#state), `.component-descendant.is-state`.

```hbs
    <button class="button  {{if (and @icon (not @label)) "is-icon-only"}}" type="button" ...attributes>
```

In the button example above, the icon only version of button has different styling so the state is made available as a CSS class.

## Events

In the case of something like a simple button component, `...attributes` on the button element makes all the HTML button events available at the component level. But for more complex components, you want to pass events up to the component level. Take a newsletter form for example. You want to expose events like onSubmit, onSuccess, onError, and so on. This allows clients using the component a way to access these events.

In a client app:

```html
<Newsletter @onSuccess={{trackEvent 'NewsletterSubscribe,Footer'}} />
```

In your component, maybe something like this? The sytntax example may not work and need updating, but in any case you should call the events from the within the component when they happen.

```
// stub these out so it's always safe to call them
onSubmitClick: () => {},
onSuccess: () => {},
onFailure: () => {},

submitNewsletter() {
  this.onSubmitClick(); // button clicked
  try {
    let response = yield fetch(subscribeEndpoint, fetchOptions)
    .then((response) => {
      handleResponse(response)
      .then(this.onSuccess); // subscribe succeeded
    });
  } catch({ detail }) {
    this.onFailure() // subscribe failed
    return detail;
  }
}
```


## Documenting your component's code

Use YUIdoc comments to document public properties in the component's `js` file. Include all properties that can be passed into a component here, even ones that are only referenced in code in the `hbs` templates.

YUIdoc examples for ember classes and components:
https://github.com/ember-learn/ember-cli-addon-docs-yuidoc#documenting-components

Example component YUIdoc comment:
``````js
/**
  Component description.....

  Usage:
  ```
  <NyprAButton>
    Click Me
  </NyprAButton>
  ```

  @class nypr-a-button
  @yield {Block} block
*/
export default Component.extend({
//...
``````

Example property YUIDoc comment:
```js
/**
Title for the component, plain text.

@argument title
@type {String}
*/
title: '',
```

## Style documentation

Add documentation for your modifier classes and state based classes to your component's mdx story page

```md
## Modifier Classes

| Class | Usage |
|---|---|
| `.mod-secondary` | Style this button as a secondary button. |
| `.mod-flat` | Style this button as a flat button. |
| `.mod-small` | Use the small variant for this button. Does not apply to flat buttons. |
| `.mod-large` | Use the large variant for this button. Does not apply to flat buttons. |

## State Classes

| Class | State |
|---|---|
| `.is-icon-only` | Applied automatically to a buttons with an icon but no label. |
```


# Testing your Component

Don't forget to write tests for your component.

## Test different component states

Test the rendering of various states and conditions you have in your template to make sure the resulting DOM output is correct. For example in the case of the button you might want to test a button with only a label, and a button with only an icon, and a button with both.

```js
  test('it renders a button with a label and an icon', async function(assert) {
    await render(hbs`<Button @label='Play' @icon='audio' />`);

    // assertions
  });

  test('it renders a button with a label only', async function(assert) {
    await render(hbs`<Button @label='Play' />`);

    // assertions
  });

  test('it renders a button with an icon only', async function(assert) {
    await render(hbs`<Button @icon='audio' @alt='Play' />`);

    // assertions
  });

  etc.
```

[`qunit-dom`](https://github.com/simplabs/qunit-dom/blob/master/API.md) is available for your test assertions.

```js
  test('it renders a button with a label and an icon', async function(assert) {
    await render(hbs`<Button @label='Play' @icon='audio' />`);

    assert.dom('.button-label').exists();
    assert.dom('.button-label').hasText('Play');
    assert.dom('svg.button-icon').exists();
  });
```

## Test for accessibility


For rendering tests like these you should also use [ember-a11y-testing](https://github.com/ember-a11y/ember-a11y-testing) to test the accessibility of your component.

Import the a11yAudit helper at the top of your test file,

```js
import a11yAudit from 'ember-a11y-testing/test-support/audit';
```

and include this code in your tests,

```js
    a11yAudit(this.element).then(() => {
      assert.ok(true, 'no a11y errors found!');
    });
```
like this.

```js
  test('it renders a button with a label and an icon', async function(assert) {
    await render(hbs`<Button @label='Play' @icon='audio' />`);

    assert.dom('.button-label').exists();
    assert.dom('.button-label').hasText('Play');
    assert.dom('svg.button-icon').exists();

    a11yAudit(this.element).then(() => {
      assert.ok(true, 'no a11y errors found!');
    });
  });
```

## Test component events

You should also test any events you pass out of your component to make sure they are firing correctly.

```js
   test('the onClick event works', async function(assert) {
    assert.expect(1);

    let clickHandler = function() {
      assert.ok('click handler was triggered')
    };

    this.set('clickHandler', clickHandler);

    await render(hbs`<Button onClick={{clickHandler}} />`);

    await click('.button');
  });
```

This example is just testing the standard dom `onClick` event, as passed to the template's button element by `...atributes`, but a similar pattern would work for an event provided by a component. (e.g. `<Newsletter @onSubscribe={{subscribeHandler}} />`)

You *don't* need to test all the style based variations, like the large button and small button classes.
